/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.matching;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import cgl.narada.event.TemplateProfileAndSynopsisTypes;
import cgl.narada.matching.integer.IntegerProfile;
import cgl.narada.matching.regex.RegExProfile;
import cgl.narada.matching.string.StringProfile;
import cgl.narada.matching.tagvaluepairs.TagValueProfile;
import cgl.narada.matching.xmlxpath.XPathProfile;

/**
 * This class encapsulates a profile propagation request
 * 
 * @author Shrideep Pallickara 
 * $Date$
 * $Revision$
 */

public class ProfileRequest {

    private boolean isFromClient = false;

    private boolean addProfile;

    private Profile profile;

    private byte[] toReach = null;

    private byte[] traversedSoFar = null;

    private int level = -1;

    private String moduleName = "ProfileRequest: ";

    // HG: Security Stuff...
    private boolean securityTokenIncluded = false;
    private byte[] signedSecurityToken = null;

    public ProfileRequest(Profile profile, boolean addProfile) {
        this.profile = profile;
        this.addProfile = addProfile;
        isFromClient = true;
    }

    public ProfileRequest(Profile profile, boolean addProfile, int level) {
        this.profile = profile;
        this.addProfile = addProfile;
        this.level = level;
    }

    public ProfileRequest(byte[] marshalledBytes) {
        ByteArrayInputStream baInputStream = new ByteArrayInputStream(
                marshalledBytes);
        DataInputStream din = new DataInputStream(baInputStream);

        try {
            /** Unmarshall the headers first */
            din.readByte();
            /** Protocol.DATA */
            din.readByte();
            /** Protocol.PROFILE_PROPAGATION */

            /** UnMarshall the profile bytes next */
            int profileType = din.readInt();
            byte[] profileBytes = new byte[din.readInt()];
            din.readFully(profileBytes);
            profile = reconstructProfile(profileType, profileBytes);

            /** UnMarshall the addProfile next */
            addProfile = din.readBoolean();

            /** UnMarshall whether it is from client or not */
            isFromClient = din.readBoolean();

            if (!isFromClient) {
                /** UnMarshall the level propagation */
                level = din.readInt();

                /** UnMarshall the toReach bytes */
                toReach = new byte[din.readInt()];
                din.readFully(toReach);

                /** UnMarshall the traversed so far bytes */
                traversedSoFar = new byte[din.readInt()];
                din.readFully(traversedSoFar);
            }
            /** end if (!client) */

            /** UnMarshal the signedSecurityToken */
            securityTokenIncluded = din.readBoolean();
            if (securityTokenIncluded) {
                int sstLen = din.readInt();
                signedSecurityToken = new byte[sstLen];
                din.readFully(signedSecurityToken);
            }

        } catch (IOException ioe) {
            System.out.println(moduleName + "Error unmarshalling stream" + ioe);
        } /* end try-catch */

    }

    /**
     * A utility method to reconstruct profile from the supplied bytes and the
     * profile type information
     */
    private Profile reconstructProfile(int profileType, byte[] profileBytes) {
        if (profileType == TemplateProfileAndSynopsisTypes.STRING) {
            return new StringProfile(profileBytes);
        }

        if (profileType == TemplateProfileAndSynopsisTypes.INTEGER) {
            return new IntegerProfile(profileBytes);
        }

        if (profileType == TemplateProfileAndSynopsisTypes.XML) {
            return new XPathProfile(profileBytes);
        }

        if (profileType == TemplateProfileAndSynopsisTypes.REGULAR_EXPRESSIONS) {
            return new RegExProfile(profileBytes);
        }

        if (profileType == TemplateProfileAndSynopsisTypes.TAG_VALUE_PAIRS) {
            return new TagValueProfile(profileBytes);
        }

        System.out.println(moduleName
                + "Unable to unmarshall the profileBytes ");
        return null;
    }

    /** Indicates if this profile propagation request is from a client */
    public boolean isFromClient() {
        return isFromClient;
    }

    /**
     * Indicates if the profile propagation request refers to a profile addition
     * or removal
     */
    public boolean isAddProfile() {
        return addProfile;
    }

    public Profile getProfile() {
        return profile;
    }

    /**
     * If the level return is -1 it means the value was never changed after its
     * initialization.
     */
    public int getLevel() {
        return level;
    }

    public byte[] getDestinationsToReach() {
        return toReach;
    }

    public byte[] getDestinationsTraversedSoFar() {
        return traversedSoFar;
    }

    public void setDestinationsToReach(byte[] toReach) {
        this.toReach = toReach;
    }

    public void setDestinationsTraversedSoFar(byte[] traversedSoFar) {
        this.traversedSoFar = traversedSoFar;
    }

    
    /**
     * This method returns a serialized representation of the profile
     * propagation Request
     */
    public byte[] getBytes() {
        ByteArrayOutputStream baOutputStream = new ByteArrayOutputStream();
        DataOutputStream dout = new DataOutputStream(baOutputStream);

        try {
            /** Marshall the headers first */
            dout.writeByte(cgl.narada.protocol.Protocol.DATA);
            dout.writeByte(cgl.narada.protocol.Protocol.PROFILE_PROPAGATION);

            /** Marshall the profile bytes next */
            dout.writeInt(profile.getProfileType());
            byte[] profileBytes = profile.getBytes();
            dout.writeInt(profileBytes.length);
            dout.write(profileBytes);

            /** Marshall the addProfile next */
            dout.writeBoolean(addProfile);

            /** Marshall whether it is from client or not */
            dout.writeBoolean(isFromClient);

            if (!isFromClient) {
                /** Marshall the level propagation */
                dout.writeInt(level);

                /** Marshall the toReach bytes */
                dout.writeInt(toReach.length);
                dout.write(toReach);

                /** Marshall the traversed so far bytes */
                dout.writeInt(traversedSoFar.length);
                dout.write(traversedSoFar);

                dout.flush();
            }
            /** end if (!client) */

            /** Marshal the signedSecurityToken */
            dout.writeBoolean(securityTokenIncluded);
            if (securityTokenIncluded) {
                dout.writeInt((signedSecurityToken.length));
                dout.write(signedSecurityToken);
            }

        } catch (IOException ioe) {
            System.out.println(moduleName + "Error marshalling stream" + ioe);
        } /* end try-catch */
        return baOutputStream.toByteArray();
    }

    // HG: Security Stuff...

    public void setSignedSecurityToken(byte[] sst) {
        if (signedSecurityToken == null)
            signedSecurityToken = sst;
        securityTokenIncluded = true;
    }

    public byte[] getSignedSecurityToken() {
        return signedSecurityToken;
    }

    public boolean hasSignedSecurityToken() {
        return securityTokenIncluded;
    }

}
